/**
 * 自己实现一个Promise,Promise是一个类，传入一个函数为参数，函数中参数为resolve和reject,函数立即调用
 * Promise有3个状态， pending, fulfilled, rejected, 且 只能由 pending => fulfilled 或 pending => rejected, fulfilled 和 rejected 不能再修改状态
 * then 方法有两个回调函数，第一个为成功后执行，第二个为失败后执行，并且参数分别为 resolve 和 reject 中传的值
 * 如果Promise中有异步调用，例如 setTimeout ，但是then方法会立即调用，此时状态为Pending，不会执行成功和失败回调，所以要将成功和失败回调存储，等到有了结果的时候调用
 *  promise 的 then 方法可以多次调用，所以将成功和失败的回调函数处理成数组
 * then 方法可以链式调用， 所以 then 方法返回一个 promise 对象，并且将 then 中的值返回
 * then 方法中返回的 promise 对象不能是自己
 * 除了 resolve 和 reject ，promise 还要能捕获错误
 * then 方法可以不传参数， 将值再下一个 then 方法中获取
 * promise.all 方法传入一个数组
 */

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    // 使用 try{}catch(){} 捕获错误
    try {
      // 立即执行
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }
  // 初始状态
  status = PENDING
  // 成功的值
  value = undefined
  // 失败的理由
  reason = undefined
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []
  resolve = (value) => {
    // 判断状态是否为pending
    if (this.status !== PENDING) return
    // 修改状态为成功
    this.status = FULFILLED
    // 成功的值
    this.value = value
    // 成功以后查看有没有成功回调，有则调用
    // this.successCallback && this.successCallback(value)
    while (this.successCallback.length) this.successCallback.shift()()
  }
  reject = (reason) => {
    // 判断状态是否为pending
    if (this.status !== PENDING) return
    // 修改状态为失败
    this.status = REJECTED
    // 失败的理由
    this.reason = reason
    // 失败后查看有没有失败回调，有则调用
    // this.failCallback && this.failCallback(reason)
    while (this.failCallback.length) this.failCallback.shift()()
  }
  // then 方法的参数给默认值
  then(fulfilledCallback = value => value, rejectedCallback = reason => { throw reason }) {
    // 返回一个promise对象
    let p2 = new MyPromise((resolve, reject) => {
      // then 方法中也要捕获错误
      // 如果成功执行第一个，如果失败执行第二个
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            // 拿到返回值，判断是普通值还是 promise 对象，
            // 普通值直接resolve， promise 对象将结果resolve 或 reject
            const successValue = fulfilledCallback(this.value)
            // 将p2 传进去，和值做对比，如果是同一个promise对象，则要报错
            // 但是在这里p2刚定义还拿不到，所以使用setTimeout包裹起来
            thenValue(p2, successValue, resolve, reject)
          } catch (err) {
            reject(err)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const rejectValue = rejectedCallback(this.reason)
            thenValue(p2, rejectValue, resolve, reject)
          } catch (err) {
            reject(err)
          }
        }, 0)
      } else {
        // 等待的时候，将两个回调存储起来方便有了结果后调用
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              const successValue = fulfilledCallback(this.value)
              thenValue(p2, successValue, resolve, reject)
            } catch (err) {
              reject(err)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              const rejectValue = rejectedCallback(this.reason)
              thenValue(p2, rejectValue, resolve, reject)
            } catch (err) {
              reject(err)
            }
          }, 0)
        })
      }


    })
    return p2
  }
  // finally 方法返回一个promise对象，这个对象会将前边promise的结果原样返回
  // 如果 finally 方法中 return 了一个promise，需要等待这个promise执行完毕再执行后边的 .then
  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, err => {
      return MyPromise.resolve(callback()).then(() => { throw err })
    })
  }
  catch(callback){
    return this.then(undefined, callback)
  }
  static all(args) {
    let results = []
    let index = 0
    // all 方法返回的也是也是 promise对象
    let newPromise = new MyPromise((resolve, reject) => {

      function addData(key, value) {
        results[key] = value
        index++
        if (index >= args.length) {
          resolve(results)
        }
      }
      // 最终返回的是一个数组
      // 遍历数组，先判断每一项是否为 promise 对象，不是直接放入结果数组
      for (let i = 0; i < args.length; i++) {
        let current = args[i]
        if (current instanceof MyPromise) {
          // value 为promise对象
          current.then((value) => {
            addData(i, value)
          }, err => {
            reject(err)
          })
        } {
          // value 是普通值
          addData(i, current)
        }
      }
    })
    return newPromise

  }
  // resolve 方法传入一个参数，返回一个promise对象
  // 参数如果是普通值转换成promise对象，如果是promise对象原样返回
  static resolve(value) {
    if (value instanceof MyPromise) return value
    return new Promise((resolve) => resolve(value))
  }
  // reject 方法传入一个参数，返回一个promise对象
  // 参数如果是普通值转换成promise对象，用reject返回，如果是promise对象将结果reject
  static reject(value) {
    return new MyPromise((resolve, reject) => {
      if (value instanceof MyPromise) {
        value.then(reject, reject)
      } else {
        // 普通值
        reject(value)
      }
    })
  }

}

function thenValue(p2, value, resolve, reject) {
  if (value instanceof MyPromise) {
    if (p2 == value) {
      reject(new TypeError('ffsffdssf'))
      return
    }
    value.then(resolve, reject)
  } else {
    // 普通值的时候直接 resolve
    resolve(value)
  }
}




let promise = new MyPromise((resolve, reject) => {
  // setTimeout(() => {
  //   resolve('chenggong...')
  // }, 2000)
  // throw Error('cuowu')
  resolve('chenggong')
  // reject('shibai')
})
// MyPromise.resolve(promise).then(value => { console.log('resolve', value) })
// MyPromise.reject(promise).then(value => { console.log('resolve', value) }, err => {console.log('reject', err)})

let promise2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('111...')
    // reject('111')
  }, 2000)
  // resolve('111')
  // reject('111')
})

// MyPromise.all([promise, promise2, 'a']).then().then().then(value => {
//   console.log(value)
// }, err => {
//   console.log(err, 'err')
// })

promise.finally(() => {
  console.log('finally')
  return promise2
}).then(value => {
  console.log(1, value)
}, (err) => {
  console.log(1, err)
})

// promise3.then(v => {
//   console.log(v, 'lianshi')
// }, (err) => {
//   console.log(err, 'err-lianshi')
// })

// promise.then(value => {
//   console.log(value)
// }, (err) => {
//   console.log(err)
// })

// promise.then(value => {
//   console.log(value)
// }, (err) => {
//   console.log(err)
// })

